
/***************************************************************************
 *   sakcab 
 *                                                                         *
 *   Copyright (C) 2010 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/


#include <iostream>
#include <fstream>
#include <string>

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>

#include <boost/program_options.hpp>
namespace po = boost::program_options;


#include <pthread.h>
#include <signal.h>

#include "bocat/common/base64.hpp"

#include "client.hpp"


int main(int argc, char* argv[])
{
    std::string host;
    std::string port;

	std::string	key_put;
	std::string	key_get;
	std::string	key_del;

	std::string	data;
	std::string	file;

	bool	decoded = false;

    try {
        po::options_description desc("Allowed options");
        desc.add_options()
            ("help,h", "produce help message")
            ("host", po::value<std::string>(&host)->default_value("127.0.0.1"), "server ip/hostname")
            ("port", po::value<std::string>(&port)->default_value("5137"), "server port")

            ("put", po::value<std::string>(&key_put), "put key and data into server")
            ("get", po::value<std::string>(&key_get), "get key and data from server")
            ("del", po::value<std::string>(&key_del), "delete key and data from server")

            ("data", po::value<std::string>(&data), "data value")
            ("file", po::value<std::string>(&file), "data file name")

            ("decode", "decode data (from base64)")
        ;

        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);

        if (vm.count("help")) {
            std::cout << desc << "\n";
            return 1;
        }
		
        if (vm.count("decode")) {
            decoded = true;
        }
		
		// put|get|del must be exist
		if ((vm.count("put") + vm.count("get") + vm.count("del")) < 1 ) {
			std::cout << "\none of put|get|del argument must be set\n\n";
            std::cout << desc << "\n";
            return 1;
		}

		// put|get|del must be exclude
		if ((vm.count("put") + vm.count("get") + vm.count("del")) > 1 ) {
			std::cout << "\nonly one of put|get|del argument must be set\n\n";
            std::cout << desc << "\n";
            return 1;
		}

		if ((vm.count("file") + vm.count("data")) > 1) {
			std::cout << "\nonly one of data file or data string can be provided\n\n";
            std::cout << desc << "\n";
            return 1;
		}

		if ((vm.count("put") > 0) && ((vm.count("file") + vm.count("data")) < 1)) {
			std::cout << "\ndata file or data string must be specified to put\n\n";
            std::cout << desc << "\n";
            return 1;
		}

    }
    catch(std::exception& e) {
        std::cerr << "error: " << e.what() << "\n";
        return 1;
    }
    catch(...) {
        std::cerr << "Exception of unknown type!\n";
        return 2;
    }


	try
	{
		// connect to  server

		int	port_i = boost::lexical_cast< int >( port );
		sakcab::client::Client	c( host.c_str(), port_i );
		std::cout << "connect to sakcab-server " << host << ":" << port << " ...\n";

		if (key_put.size() > 0) {
			if (data.size() < 1) {
				// data is null, read it from file
				boost::filesystem::path conf_path( file );
				if ( !boost::filesystem::exists( conf_path ) ) { 
					std::cerr << "data file " << file << " does NOT exist.\n";
					return 3;
				}   
				if ( !boost::filesystem::is_regular_file( conf_path ) ) { 
					std::cerr << "data file " << file << " is NOT a regular file.\n";
					return 4;
				}   

				std::ifstream	ifs( file.c_str() );
				ifs.seekg(0, std::ios::end);
				size_t	length = ifs.tellg();
				ifs.seekg(0, std::ios::beg);

				char*	buf = new char[ length ];
				ifs.read( buf, length );
				ifs.close();

				data.assign(buf, length);
				delete [] buf;
			}

			std::string	data_b64 = bocat::common::base64_encode( data );

			std::cout << " - put key '" << key_put << "' ...\n";
			int rc = c.insert( key_put, data_b64 );
			if ( rc < 0 ) {
				std::cout << " - put key '" << key_put << "' failed (RC:" << rc << ").\n";
			}
			else {
				std::cout << " - put key '" << key_put << "' done.\n";
			}
		}
		else if (key_get.size() > 0) {
			std::cout << " - get key '" << key_get << "' ...\n";
			std::string	data;
			int rc = c.find( key_get, data );
			if ( rc < 0 ) {
				std::cout << " - get key '" << key_get << "' failed (RC:" << rc << ").\n";
			}
			else if ( rc == 0 ) {
				std::cout << " - get key '" << key_get << "' NOT-FOUND.\n";
			}
			else {
				std::cout << " - get key '" << key_get << "' done.\n";
				std::cout << "   ----==== data begin ====----\n";
				if (decoded) {
					std::cout << bocat::common::base64_decode< std::string >(data) << std::endl;
				}
				else {
					std::cout << data << std::endl;
				}
				std::cout << "   ====---- data  end  ----====\n";
			}
		}
		else if (key_del.size() > 0) {
			std::cout << " - delete key '" << key_del << "' ...\n";
			int rc = c.erase( key_del );
			if ( rc < 0 ) {
				std::cout << " - delete key '" << key_del << "' failed (RC:" << rc << ").\n";
			}
			else {
				std::cout << " - delete key '" << key_del << "' done.\n";
			}
		}

		std::cout << "done.\n";
	}
	catch (std::exception& e)
	{
		std::cerr << "exception: " << e.what() << "\n";
	}

	return 0;
}


